{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(page-working)=\n",
    "\n",
    "# Working with Norse\n",
    "\n",
    "For us, Norse is a tool to accelerate our own work within spiking neural networks (SNN).\n",
    "This page serves to describe the fundamental ideas behind the Python code in Norse and\n",
    "provide you with specific tools to become productive with SNN.\n",
    "\n",
    "We will start by explaining some basic terminology, describe a *suggestion* to how Norse\n",
    "can be approached, and finally provide examples on how we have solved specific problems\n",
    "with Norse.\n",
    "\n",
    "**Table of content**\n",
    "\n",
    "1. Terminology\n",
    "2. Norse workflow\n",
    "3. Solving deep learning problems with Norse\n",
    "\n",
    "\n",
    ":::{note}\n",
    "You can execute the code below by hitting <i class=\"fas fa-rocket\"></i> above and pressing <i class=\"fas fa-play\"></i> Live Code.\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Terminology\n",
    "\n",
    "### Events and action potentials\n",
    "\n",
    "```{figure} ../images/book-fig-spikes.png\n",
    "---\n",
    "figclass: margin\n",
    "name: fig_working_spikes\n",
    "---\n",
    "Illustration of discrete events, or *spikes*, from 10 neurons (y-axis) over 40 timesteps (x-axis) with events shown in white.\n",
    "```\n",
    "\n",
    "Neurons are famous for their efficacy because they only react to sparse (rare) events called [spikes or action potentials](https://en.wikipedia.org/wiki/Action_potential).\n",
    "In a spiking network *less than* $2\\%$ of the neurons are active at once.\n",
    "In Norse, therefore, we mainly operate on **binary tensors** of 0's (no events) and 1's (spike!).\n",
    "{numref}`fig_working_spikes` illustrates such a random sampled data with exactly $2\\%$ activation.\n",
    "\n",
    "\n",
    "### Neurons and neuron state\n",
    "Neurons have parameters that determine their function. For example, they have a\n",
    "certain membrane voltage that will lead the neuron to spike *if* the voltage is\n",
    "above a threshold. Someone needs to keep track of that membrane voltage. If we \n",
    "wouldn't, the neuron membrane would never update and we would never get any\n",
    "spikes. In Norse, we refer to that as the **neuron state**.\n",
    "\n",
    "In code, it looks like this:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import norse.torch as norse\n",
    "\n",
    "cell = norse.LIFCell()\n",
    "data = torch.ones(1)\n",
    "spikes, state = cell(data)        # First run is done without any state\n",
    "# ...\n",
    "spikes, state = cell(data, state) # Now we pass in the previous state"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::{figure-md} started-lif\n",
    "<img src=\"../images/lif_integration.gif\" alt=\"no-current\" class=\"bg-primary mb-1\" >\n",
    "\n",
    "Three examples of how the LIF neuron model responds to three different, but constant, input currents: 0.0, 0.1, and 0.3. At 0.3, we see that the neuron fires a series of spikes, followed by a membrane \"reset\".\n",
    "Note that the neuron parameters are non-biological and that the memebrane voltage threshold is 1.\n",
    ":::\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "States typically consist of two values: `v` (voltage), and `i` (current).\n",
    "\n",
    "* Voltage (`v`) illustrates the difference in \"electric tension\" in the neuron membrane. The higher the value, the more tension and better chance to arrive at a spike. In {numref}`fig_working_ap` the spike arrives at the peak of the curve, followed by an immediate reset and recovery. This is crucial for emitting spikes: if the voltage never increases - no spike!\n",
    "* Current (`i`) illustrates the incoming current, which will be integrated into the membrane potential `v` and decays over time.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Neuron dynamics and time\n",
    "\n",
    "Norse solves two of the hardest parts about running neuron simulations: neural equations and temporal dynamics.\n",
    "We provide a long list of neuron model implementations, as listed [in our documentation](https://norse.github.io/norse/generated/norse.torch.html#neuron-models) that is free to plug'n'play.\n",
    "\n",
    "For each model, we distinguish between **time** and **recurrence** as follows (using the [Long short-term memory neuron model](https://arxiv.org/abs/1803.09574) as an example):\n",
    "\n",
    "|                           | *Without* time      |  *With* time    |\n",
    "| :------------------------ | ------------------- | --------------: |\n",
    "| ***Without* recurrence**  | `LSNNCell`          | `LSNN`          |\n",
    "| ***With* recurrence**     | `LSNNRecurrentCell` | `LSNNRecurrent` |\n",
    "\n",
    "\n",
    "In other words, the `LSNNCell` is *not* recurrent, and expects the input data to *not* have time, while the\n",
    "`LSNNRecurrent` *is* recurrent and expects the input to have time in the *first* dimension."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Norse workflow\n",
    "\n",
    "Norse is meant to be used as a library. Specifically, that means taking parts of it and\n",
    "remixing to fit the needs of a specific task. \n",
    "We have tried to provide useful, documented, and correct features from the spiking neural network domain, such\n",
    "that they become simple to work with.\n",
    "\n",
    "The two main differences from artificial neural networks is 1) the state variables containing the neuron parameters\n",
    "and 2) the temporal dimension (see [](page-spiking)). \n",
    "Apart from that, Norse works like you would expect any PyTorch module to work.\n",
    "\n",
    "When working with Norse we recommend that you consider two things\n",
    "\n",
    "1. Neuron models \n",
    "2. Learning algorithms and/or plasticity models \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### Deciding on neuron models\n",
    "\n",
    "The choice of neuron model depends on the task. Should the model be biologically plausible? Computationally efficient? ...\n",
    "Two popular choices of models are the [leaky integrate-and-fire neuron model](https://norse.github.io/norse/norse.torch.html#leaky-integrate-and-fire-lif), which will provide spiking output of either `0`s or `1`s. Another model is the [leaky integrator](https://norse.github.io/norse/norse.torch.html#leaky-integrator), which will provide a voltage scalar output.\n",
    "Many more neuron models exist and can be found in our documentation: https://norse.github.io/norse/norse.torch.html#neuron-models\n",
    "\n",
    "### Deciding on learning/plasiticy models\n",
    "\n",
    "Optimization can be done using PyTorch's gradient-based optimizations, as seen in the [MNIST task](https://github.com/norse/norse/blob/master/norse/task/mnist.py#L100). We have implemented [SuperSpike](https://arxiv.org/abs/1705.11146v2) and many other [surrogate gradient methods](https://arxiv.org/abs/1901.09948) that lets you seamlessly integrate with Norse. The surrogate gradient methods are documented here: https://norse.github.io/norse/norse.torch.functional.html#threshold-functions\n",
    "\n",
    "If you require biological/local learning, we support plasticity via [STDP](https://norse.github.io/norse/generated/norse.torch.functional.stdp.html#module-norse.torch.functional.stdp) and [Tsodyks-Markram](https://norse.github.io/norse/generated/norse.torch.functional.tsodyks_makram.html#module-norse.torch.functional.tsodyks_makram) models."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Examples of deep learning problems in Norse\n",
    "\n",
    "Norse can be applied immediately for both fundamental research and deep learning problems.\n",
    "\n",
    "To **port existing deep learning problems**, we can simply 1) replicate ANN architecture, 2) lift the signal in time (to allow the neurons time to react to the input signal), and 3) replace the ANN activation functions with SNN activation functions.\n",
    "\n",
    "We have several examples of that in our [tasks section](https://norse.github.io/norse/pages/tasks.html), and [MNIST](https://github.com/norse/norse/blob/master/norse/task/mnist.py) is one of them; here we 1) build a convolutional network, 2) convert the MNIST dataset into sparse discrete events and 3) solve the task with LIF models, achieving >90% accuracy.\n",
    "\n",
    "We can also **replicate experiments from the literature**, as shown in the [memory task](https://github.com/norse/norse/blob/master/norse/task/memory.py) example. Here we use [adaptive long short-term spiking neural networks](https://github.com/IGITUGraz/LSNN-official) to solve temporal memory problems."
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "1d7c6e90a74384adccd95650de7b60ccf01b88e91c61eab2330936fc6c29e88b"
  },
  "kernelspec": {
   "display_name": "Python 3.8.10 64-bit ('norse': venv)",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
